﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.IO;
using System.Text.RegularExpressions;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Metadata;
using QueueManager.Helper;
using QueueManager.Helper.Crm;
using QueueManager.Helper.vo;

namespace QueueManager.Service
{
  public class QueueMonitor
  {
    private Regex _objRegEx;
    private Regex _objParser;

    /// <summary>
    /// Processes a queue and creates cases
    /// </summary>
    /// <param name="queueSettings">The settings of the queue to process.</param>
    public void ProcessQueue(QueueSetting queueSettings)
    {
      try
      {
        var serverConnect = new CustomCrmServiceHelper();
        var config = serverConnect.GetServerConfiguration();

        // Connect to the Organization service. 
        // The using statement assures that the service proxy will be properly disposed.
        using (var serviceProxy = CustomCrmServiceHelper.GetOrganizationProxy(config))
        {
          // This statement is required to enable early-bound type support.
          serviceProxy.EnableProxyTypes();
          var service = (IOrganizationService)serviceProxy;

          var crmHelper = new CrmHelper(service);

          //Get information about the queue
          var queue = crmHelper.GetQueue(new Guid(queueSettings.QueueId));
          if (queue == null)
          {
            LogHelper.Log(string.Format("Can't find queue specified in '{0}'.", queueSettings.Name), null, LogHelper.LogType.Warning);
            return;
          }

          //Get queue items from the queue
          var queueItems = crmHelper.GetQueueItems(queue, queueSettings.RemoveRepliesFromQueue);

          //Loop through result
          foreach (var item in queueItems)
          {
            LogHelper.Output("", false);

            //Retrieve the e-mail
            var email = crmHelper.GetEmail(item.ObjectId.Id, item.Title);

            //Process the email
            if (!ContinueProcessEmail(queueSettings, queue, service, email, item))
              continue;

            //If partyid is nothing then the e-mail isn't related to a contact
            var objIncomingEnt = GetIncomingEntity(service, queueSettings, email);

            //Either the sender as been resolved and we can carry on or we need to create it
            if (objIncomingEnt != null)
            {
              if (objIncomingEnt.EntityLogicalName == Contact.EntityLogicalName || objIncomingEnt.EntityLogicalName == Account.EntityLogicalName || objIncomingEnt.EntityLogicalName == SystemUser.EntityLogicalName)
              {
                //Only create cases for contact/accounts
                //We now have a customer so let's create a new case/incident
                CreateCase(service, objIncomingEnt, email, queueSettings, queue);

                //Delete the queueItem
                crmHelper.DeleteQueueItem(item);
              }
              else
              {
                if (string.IsNullOrEmpty(objIncomingEnt.EntityLogicalName))
                {
                  //Should we send verification e-mail?

                  if (queueSettings.SendVerificationMail)
                  {
                    LogHelper.Output("Sending verification e-mail...");

                    //Route the email to the temp queue
                    crmHelper.RouteEMail(email, new Guid(queueSettings.QueueId), new Guid(queueSettings.TempQueueId));

                    //Create a unique identifier that we can use to retrieve the mail again.
                    var strUniqueId = DateTime.Now.Ticks.ToString(CultureInfo.InvariantCulture);

                    //Store this in the trackingtoken attribute
                    crmHelper.AddTrackingTokenToEmail(email, strUniqueId);

                    //The Url sent out consists of "key" (the token), "s" source queue and "t" target queue
                    //needed in order to move it back once updated.
                    var strUrl = string.Format("{0}?key={1}&s={2}&t={3}", queueSettings.VerificationMailUrl, strUniqueId, queueSettings.TempQueueId.ToLower(), queueSettings.QueueId.ToLower());

                    //Send an e-mail
                    //Also get the owner e-mail address to be used for notification.
                    var queueOwner = crmHelper.GetSystemUser(queue.OwnerId.Id);
                    crmHelper.SendNetMail(queueSettings.VerificationMailSubject, File.ReadAllText(queueSettings.VerificationMailTemplate).Replace("{url}", strUrl), email.From.First().AddressUsed, queueSettings.VerificationMailBcc, Convert.ToString((queueSettings.VerificationMailFrom == "*" ? queueOwner.InternalEMailAddress : queueSettings.VerificationMailFrom)), queueSettings.VerificationMailUseHtml);

                    LogHelper.Output("Verification e-mail sent.");
                    //Or should we simply create the contact?
                  }
                  else if (queueSettings.CreateContact)
                  {
                    LogHelper.Output("Automatically creating contact...");

                    //Create a new contact using the information provided
                    var contact = new Contact();

                    //Get the name from the e-mail info. Use submitted by if it exists as the full name of 
                    //the sender might be there. If not, use e-mail address.
                    var name = crmHelper.ResolveNames(string.IsNullOrEmpty(email.SubmittedBy) ? email.From.First().AddressUsed : email.SubmittedBy);

                    //Set properties
                    contact.FirstName = name.FirstName;
                    contact.LastName = name.LastName;
                    contact.EMailAddress1 = email.From.First().AddressUsed;

                    //Attempt to resolve the parent account automatically
                    if (queueSettings.ResolveParent)
                    {
                      crmHelper.ResolveParentAccount(contact, email.From.First().AddressUsed, queueSettings.ResolveParentNotify, queueSettings.ResolveParentFrom);
                    }

                    //Create contact
                    var contactId = crmHelper.Create(contact);

                    LogHelper.Output("Contact '{0}' created.", name.FirstName + " " + name.LastName);

                    //Create a generic representation of the type used for parsing etc
                    objIncomingEnt = new IncomingEntity
                    {
                      FirstName = contact.FirstName,
                      LastName = contact.LastName,
                      Id = contactId,
                      EntityLogicalName = Contact.EntityLogicalName
                    };

                    //Check to see if parent account options are alright
                    if (!SetCaseCustomer(contact, objIncomingEnt, queueSettings))
                    {
                      objIncomingEnt.CaseEntityId = contactId;
                      objIncomingEnt.CaseEntityType = Contact.EntityLogicalName;
                    }

                    //Create the case
                    CreateCase(service, objIncomingEnt, email, queueSettings, queue);

                    //Delete the queueItem
                    crmHelper.DeleteQueueItem(item);

                  }
                }
              }
            }
          }
        }

      }
      catch (Exception ex)
      {
        LogHelper.Log("Unhandled exception occurred in ProcessQueue", ex, LogHelper.LogType.Exception);
      }

    }

    private static IncomingEntity GetIncomingEntity(IOrganizationService service, QueueSetting queueSettings, Email email)
    {
      IncomingEntity incomingEntity = null;
      Contact contact;
      var crmHelper = new CrmHelper(service);
      if (email.From.First().PartyId == null)
      {
        LogHelper.Output("No contact found. Trying to match using alternative e-mail address.");

        incomingEntity = new IncomingEntity();

        //Get contacts
        var contacts = crmHelper.GetContacts(email.From.First().AddressUsed);

        if (contacts != null && contacts.Count > 0)
        {
          LogHelper.Output("Found contact.");

          //Get the the contact
          contact = contacts[0];

          incomingEntity.FirstName = contact.FirstName;
          incomingEntity.LastName = contact.LastName;

          if (contact.ContactId.HasValue)
            incomingEntity.Id = contact.ContactId.Value;

          incomingEntity.EntityLogicalName = Contact.EntityLogicalName;

          //Check to see if parent account options are alright
          if (!SetCaseCustomer(contact, incomingEntity, queueSettings))
            LogHelper.Log(string.Format("Customer with e-mail address '{0}' is missing a parent account and no case will be created for the incoming e-mail.", email.From.First().AddressUsed), null, LogHelper.LogType.Warning);

          //If more than 1 contact returned then log as a warning
          if (contacts.Count > 1)
            LogHelper.Log(string.Format("Duplicates detected. More than one contact was found with the e-mail address {0}. The first one was used.", email.From.First().AddressUsed), null, LogHelper.LogType.Warning);
        }
      }
      else
      {
        //CRM only supports accounts/contacts/systemusers for cases
        if (email.From.First().PartyId.LogicalName == Contact.EntityLogicalName)
        {
          LogHelper.Output("Retrieving contact...");

          //Retrieve the contact, we need its details when parsing e-mail
          try
          {
            contact = crmHelper.GetContact(email.From.First().PartyId.Id);

            //Create a generic representation of the type used for parsing etc
            incomingEntity = new IncomingEntity
            {
              FirstName = contact.FirstName,
              LastName = contact.LastName,
              Id = email.From.First().PartyId.Id,
              EntityLogicalName = Contact.EntityLogicalName
            };

            //Check to see if parent account options are alright
            if (!SetCaseCustomer(contact, incomingEntity, queueSettings))
            {
              LogHelper.Log(string.Format("Customer with e-mail address '{0}' is missing a parent account and no case will be created for the incoming e-mail.", email.From.First().AddressUsed), null, LogHelper.LogType.Warning);
              //Set the entity to nothing to skip it
              incomingEntity = null;
            }
          }
          catch (Exception ex)
          {
            //Allow this to happen, contacts might not exist if the contact has been removed and
            //the queue item still exists.
            LogHelper.Log(string.Format("Could not retrieve contact related to e-mail '{0}'.", email.Subject), ex, LogHelper.LogType.Exception);
          }
        }
        else if (email.From.First().PartyId.LogicalName == Account.EntityLogicalName)
        {
          LogHelper.Output("Retrieving account...");

          var name = crmHelper.ResolveNames(email.SubmittedBy);

          //Create a generic representation of the type used for parsing etc
          incomingEntity = new IncomingEntity
          {
            FirstName = name.FirstName,
            LastName = name.LastName,
            Id = email.From.First().PartyId.Id,
            EntityLogicalName = Account.EntityLogicalName
          };
          incomingEntity.CaseEntityId = incomingEntity.Id;
          incomingEntity.CaseEntityType = incomingEntity.EntityLogicalName;
        }
        else if (email.From.First().PartyId.LogicalName == SystemUser.EntityLogicalName)
        {
          LogHelper.Output("e-mail is sent by an internal user. Attempting to retrieve a contact...");

          //Get contacts
          var contacts = crmHelper.GetContacts(email.From.First().AddressUsed);

          if (contacts != null && contacts.Count > 0)
          {
            LogHelper.Output("Found contact. Will create case for this contact instead.");

            //Get the the contact
            contact = contacts[0];

            //Create a generic representation of the type used for parsing etc
            incomingEntity = new IncomingEntity();
            incomingEntity.FirstName = contact.FirstName;
            incomingEntity.LastName = contact.LastName;
            incomingEntity.Id = email.From.First().PartyId.Id;
            incomingEntity.EntityLogicalName = SystemUser.EntityLogicalName;
            incomingEntity.CaseEntityId = contact.ContactId.Value;
            incomingEntity.CaseEntityType = Contact.EntityLogicalName;
          }
          else
          {
            incomingEntity = new IncomingEntity();
            incomingEntity.EntityLogicalName = "other";
          }
        }
        else
        {
          incomingEntity = new IncomingEntity();
          incomingEntity.EntityLogicalName = "other";
          LogHelper.Output("Can't create case for sender type '{0}'.", email.From.First().PartyId.LogicalName);
        }
      }
      return incomingEntity;
    }

    private bool ContinueProcessEmail(QueueSetting queueSettings, Queue queue, IOrganizationService service, Email email, QueueItem item)
    {
      //If we dont have a email, dont process
      if (email == null)
        return false;

      var crmHelper = new CrmHelper(service);

      //Process list of ignore words
      if (ContainsWordsToIgnore(email, queueSettings))
      {
        //Move the e-mail to the temp queue?
        if (queueSettings.MoveIgnored)
          crmHelper.RouteEMail(email, new Guid(queueSettings.QueueId), new Guid(queueSettings.TempQueueId));

        return false;
      }

      //Case number check
      if (queueSettings.MatchCaseTicketNumber && email.RegardingObjectId == null)
      {
        //Try and get the case if there's a match for the case number in the title.
        //Wild attempt to overcome the fact that people get confused with tracking numbers/case numbers
        var machingCase = GetMatchingCase(service, queueSettings, email);

        //If we have a match, associate the email with the case
        if (machingCase != null)
        {
          crmHelper.AssociateEmailWithCase(email, machingCase);

          crmHelper.DeleteQueueItem(item);
          return false;
        }
      }

      //Optionally remove the e-mail from the queue if the e-mail already is related to a case
      if (queueSettings.RemoveRepliesFromQueue && email.RegardingObjectId != null)
      {
        LogHelper.Output("Email identified as reply, removing it.");

        //Detach from the queue...
        crmHelper.DeleteQueueItem(item);

        //...and set to nothing so we don't continue
        return false;
      }

      //Check and make sure we are on to To-line in the e-mail. 
      if (queueSettings.AllowOnlyTo && !string.IsNullOrEmpty(queue.EMailAddress))
      {
        if (!IsSendToUs(email, queue))
        {
          LogHelper.Output("We are not in the 'to' field, ignoring e-mail.");
          crmHelper.RouteEMail(email, new Guid(queueSettings.QueueId), new Guid(queueSettings.TempQueueId));

          //Should we notify the sender?
          if (queueSettings.AllowOnlyToReply)
            NotifySender(service, email, queue, queueSettings);

          return false;
        }
      }
      return true;
    }

    private static void NotifySender(IOrganizationService service, Email email, Queue queue, QueueSetting queueSettings)
    {
      try
      {
        var crmHelper = new CrmHelper(service);
        crmHelper.SendNetMail(queueSettings.AllowOnlyToSubject, File.ReadAllText(queueSettings.AllowOnlyToTemplate).Replace("{queueaddress}", queue.EMailAddress).Replace("{subject}", email.Subject), email.From.First().AddressUsed, queueSettings.AllowOnlyToNotify, queueSettings.AllowOnlyToFrom, queueSettings.AllowOnlyToUseHtml);
      }
      catch (Exception ex)
      {
        LogHelper.Log("Error sending 'AllowOnlyTo' mail.", ex, LogHelper.LogType.Exception);
      }
    }

    /// <summary>
    /// Matches ticket number in subject line
    /// </summary>
    /// <param name="service"> </param>
    /// <param name="queueSettings"> </param>
    /// <param name="email">Return nothing if match</param>
    private Incident GetMatchingCase(IOrganizationService service, QueueSetting queueSettings, Email email)
    {
      var ticketNumber = GetTicketNumber(service, queueSettings, email);
      if (ticketNumber != "")
      {
        var crmHelper = new CrmHelper(service);
        return crmHelper.GetCase(ticketNumber);
      }

      return null;
    }

    private string GetTicketNumber(IOrganizationService service, QueueSetting queueSettings, Email email)
    {
      if (_objRegEx == null)
      {
        try
        {
          var crmHelper = new CrmHelper(service);
          var organization = crmHelper.GetOrgInfo();

          //Create reg exp that will match "[casesuffix]-[n digits]-[alpha uniquespecifier]" from org settings. Can't use String.Format here...
          _objRegEx = new Regex("\\b(0)\\b-\\d+-\\b\\w{(1)}\\b".Replace("(0)", organization.CasePrefix).Replace("(1)", organization.UniqueSpecifierLength.Value.ToString()), RegexOptions.IgnoreCase);
        }
        catch (Exception ex)
        {
          LogHelper.Log("Error constructing regexp.", ex, LogHelper.LogType.Exception);
        }
      }

      try
      {
        if (queueSettings.MatchCaseTicketNumber && _objRegEx.IsMatch(email.Subject))
          return _objRegEx.Match(email.Subject).Value;

        if (queueSettings.MatchCaseTicketNumber && _objRegEx.IsMatch(email.Description))
          return _objRegEx.Match(email.Description).Value;

      }
      catch (Exception ex)
      {
        LogHelper.Log("Error fetching case by subject.", ex, LogHelper.LogType.Exception);
        //Carry on
      }

      return "";
    }

    /// <summary>
    /// Checks the occurrance of words in the subject line.
    /// </summary>
    /// <param name="email">The e-mail to check.</param>
    /// <param name="queueSettings">The queue settings</param>
    private static bool ContainsWordsToIgnore(Email email, QueueSetting queueSettings)
    {
      //If we dont have a list, return false
      if (string.IsNullOrEmpty(queueSettings.IgnoreList))
        return false;

      //If the email has no subject, stop processing it
      if (string.IsNullOrEmpty(email.Subject))
        return false;

      foreach (var word in queueSettings.IgnoreList.Split(';'))
      {
        //The if the subject contains any of the words
        if (!email.Subject.ToLower().Contains(word.ToLower()) || word.Trim() == "") continue;
        return true;
      }
      return false;
    }

    /// <summary>
    /// Make sure we're the To line and not CC or Bcc
    /// </summary>
    /// <param name="email">The email to check</param>
    /// <param name="queue">The queue</param>
    private static bool IsSendToUs(Email email, Queue queue)
    {
      //Loop through the to array and check that we're in it
      return email.To.Any(objParty => objParty.AddressUsed.ToLower() == queue.EMailAddress.ToLower());
    }

    /// <summary>
    /// Creates a case
    /// </summary>
    /// <param name="service"> </param>
    /// <param name="entity">The customer to create the case for</param>
    /// <param name="email">The e-mail to relate</param>
    /// <param name="queueSettings">Instance of queue settings</param>
    /// <param name="queue">The queue</param>
    /// <param name="queueOwner"> </param>
    private void CreateCase(IOrganizationService service, IncomingEntity entity, Email email, QueueSetting queueSettings, Queue queue)
    {
      try
      {
        var crmHelper = new CrmHelper(service);

        //Go on and create case
        var incident = new Incident();

        LogHelper.Output("Creating case...");

        //The title should be the subject of the e-mail
        incident.Title = email.Subject;

        //All cases originates from a email
        incident.CaseOriginCode = new OptionSetValue((int)Helper.OptionSets.IncidentCaseOriginCode.Email);

        //Set the customer
        incident.CustomerId = new EntityReference(entity.CaseEntityType, entity.CaseEntityId);

        //Set the owner
        incident.OwnerId = new EntityReference(queue.OwnerId.LogicalName, queue.OwnerId.Id);

        //Should we update the responsible contact field?
        if (queueSettings.SetResponsibleContact && entity.CaseEntityType == Contact.EntityLogicalName)
          incident.ResponsibleContactId = new EntityReference(entity.CaseEntityType, entity.CaseEntityId);

        //Should the description be updated?
        if (queueSettings.UpdateCaseDescription)
        {
          //A lame attempt to remove html formatting and preserve some air in the text...
          incident.Description = crmHelper.CleanHtml(email.Description);

          //Limit length
          var objAtt = crmHelper.GetAttribute(Incident.EntityLogicalName, "description");
          if (incident.Description.Length > ((MemoAttributeMetadata)objAtt).MaxLength.Value - 1)
            incident.Description = incident.Description.Substring(0, ((MemoAttributeMetadata)objAtt).MaxLength.Value - 1);

        }

        //Update dynamic properties
        SetDynamicProperties(service, incident, queueSettings);

        //Create the case in CRM
        var caseId = crmHelper.CreateCase(incident);

        //...and retrieve it to get poplulated defaults or result of workflows etc
        incident = crmHelper.GetCase(caseId);

        LogHelper.Output("Case {0} created.", incident.TicketNumber);

        //Update the e-mail and set "regarding" to the case
        crmHelper.AssociateEmailWithCase(email, incident);

        //Send an e-mail
        if (queueSettings.SendCaseMail)
          SendCaseMail(service, entity, email, queueSettings, queue, incident);

        //Send a separate notification?
        if (queueSettings.SendCaseNotification)
          SendCaseNotification(service, entity, email, queueSettings, queue, incident);

      }
      catch (Exception ex)
      {
        LogHelper.Log("Could not create case.", ex, LogHelper.LogType.Exception);
      }

    }
    private void SendCaseNotification(IOrganizationService service, IncomingEntity entity, Email email, QueueSetting queueSettings, Queue queue, Incident incident)
    {
      var crmHelper = new CrmHelper(service);

      LogHelper.Output("Sending notification...");

      //Parse the text
      var subject = ParseCaseMail(service, queueSettings.CaseNotificationSubject, incident, entity, email, queue);
      var body = ParseCaseMail(service, File.ReadAllText(queueSettings.CaseNotificationTemplate), incident, entity, email, queue);

      var queueOwner = crmHelper.GetSystemUser(queue.OwnerId.Id);
      crmHelper.SendNetMail(subject, body, Convert.ToString((queueSettings.CaseNotificationTo == "*" ? queueOwner.InternalEMailAddress : queueSettings.CaseNotificationTo)), "", Convert.ToString((queueSettings.CaseNotificationFrom == "*" ? queueOwner.InternalEMailAddress : queueSettings.CaseNotificationFrom)), queueSettings.CaseNotificationUseHtml);

      LogHelper.Output("Notification sent.");
    }
    private void SendCaseMail(IOrganizationService service, IncomingEntity entity, Email email, QueueSetting queueSettings, Queue queue, Incident incident)
    {
      LogHelper.Output("Replying sender...");
      var crmHelper = new CrmHelper(service);

      //Parse the text
      var subject = ParseCaseMail(service, queueSettings.CaseMailSubject, incident, entity, email, queue);
      var body = ParseCaseMail(service, File.ReadAllText(queueSettings.CaseMailTemplate), incident, entity, email, queue);

      //If the setting casemail-from is a mail addrees then send using Net mail...
      if (queueSettings.CaseMailFrom.Contains("@") || queueSettings.CaseMailFrom.Contains("*"))
      {
        var queueOwner = crmHelper.GetSystemUser(queue.OwnerId.Id);
        crmHelper.SendNetMail(subject, body, email.From.First().AddressUsed, queueSettings.CaseMailNotify, queueSettings.CaseMailFrom == "*" ? queueOwner.InternalEMailAddress : queueSettings.CaseMailFrom, queueSettings.CaseMailUseHtml);
      }
      else
      {
        //...otherwise, send using CRM.
        crmHelper.SendCrmMail(subject, body, queueSettings.CaseMailFromType, new Guid(queueSettings.CaseMailFrom), entity.EntityLogicalName, entity.Id, null, incident);

        //Send a notification that a case has been created?
        if (!String.IsNullOrEmpty(queueSettings.CaseMailNotify))
        {
          var queueOwner = crmHelper.GetSystemUser(queue.OwnerId.Id);
          crmHelper.SendNetMail(subject, body, queueSettings.CaseMailNotify, null, queueSettings.VerificationMailFrom == "*" ? queueOwner.InternalEMailAddress : queueSettings.VerificationMailFrom, queueSettings.CaseMailUseHtml);
        }

      }

      LogHelper.Output("e-mail sent.");
    }

    ///// <summary>
    ///// Updates properties.
    ///// </summary>
    ///// <param name="incident"></param>
    ///// <param name="QueueSettings"></param>
    ///// <remarks></remarks>
    private void SetDynamicProperties(IOrganizationService service, Entity objCase, QueueSetting queueSettings)
    {
      try
      {
        //Loop through all properties in app.config
        var crmHelper = new CrmHelper(service);
        foreach (var property in queueSettings.CaseAttributes.Split('|'))
        {
          if (property.Trim().Length == 0)
            return;

          //Split on delimiter
          var strSplit = property.Split('#');

          if (strSplit.Length != 2)
          {
            LogHelper.Log(string.Format("Dynamic property definition incorrect: {0}", property), null, LogHelper.LogType.Warning);
          }
          else
          {
            //Get the attribute metadata
            var attribute = crmHelper.GetAttribute(Incident.EntityLogicalName, strSplit[0]);

            //Create the property
            switch (attribute.AttributeType.Value)
            {
              case AttributeTypeCode.String:
              case AttributeTypeCode.Memo:
                objCase.Attributes.Add(strSplit[0], strSplit[1]);
                break;
              case AttributeTypeCode.Integer:
                objCase.Attributes.Add(strSplit[0], int.Parse(strSplit[1]));
                break;
              case AttributeTypeCode.Double:
                objCase.Attributes.Add(strSplit[0], double.Parse(strSplit[1]));
                break;
              case AttributeTypeCode.Decimal:
                objCase.Attributes.Add(strSplit[0], decimal.Parse(strSplit[1]));
                break;
              case AttributeTypeCode.DateTime:
                objCase.Attributes.Add(strSplit[0], DateTime.Parse(strSplit[1]));
                break;
              case AttributeTypeCode.Customer:
                objCase.Attributes.Add(strSplit[0], new Guid(strSplit[1]));
                break;
              case AttributeTypeCode.Boolean:
                objCase.Attributes.Add(strSplit[0], bool.Parse(strSplit[1]));
                break;
              case AttributeTypeCode.Status:
                objCase.Attributes.Add(strSplit[0], int.Parse(strSplit[1]));
                break;
              case AttributeTypeCode.State:
                objCase.Attributes.Add(strSplit[0], strSplit[1]);
                break;
              case AttributeTypeCode.Picklist:
                objCase.Attributes.Add(strSplit[0], int.Parse(strSplit[1]));
                break;
              case AttributeTypeCode.Lookup:
                objCase.Attributes.Add(strSplit[0], new Guid(strSplit[1]));
                break;
              default:
                LogHelper.Log(string.Format("Dynamic property definition incorrect: {0}", property), null, LogHelper.LogType.Warning);
                break;
            }
          }
        }
      }
      catch (Exception ex)
      {
        LogHelper.Log("Error setting dynamic properties.", ex, LogHelper.LogType.Exception);
      }
    }

    /// <summary>
    /// Determines the case customer (either contact or parent account)
    /// </summary>
    /// <param name="contact">The contact</param>
    /// <param name="objIncomingEnt">The entity to update</param>
    /// <param name="queueSettings">Settings</param>
    /// <returns>True if OK, otherwise false (bail out)</returns>
    private static bool SetCaseCustomer(Contact contact, IncomingEntity objIncomingEnt, QueueSetting queueSettings)
    {
      //Check if the option is set
      if (queueSettings.MapCaseToAccount)
      {
        //Check to see if the contact has a parent account
        if (contact.ParentCustomerId != null)
        {
          //Then use the parent account
          objIncomingEnt.CaseEntityId = contact.ParentCustomerId.Id;
          objIncomingEnt.CaseEntityType = contact.ParentCustomerId.LogicalName;
        }
        else
        {
          //Should we assign it to the contact if the parent account is missing?
          if (queueSettings.ParentAccountMissing)
          {
            objIncomingEnt.CaseEntityId = objIncomingEnt.Id;
            objIncomingEnt.CaseEntityType = objIncomingEnt.EntityLogicalName;
          }
          else
          {
            //Bail out (no parent account).
            return false;
          }
        }
      }
      else
      {
        objIncomingEnt.CaseEntityId = objIncomingEnt.Id;
        objIncomingEnt.CaseEntityType = objIncomingEnt.EntityLogicalName;
      }

      return true;
    }

    /// <summary>
    /// Clears the temp queue according to config settings
    /// </summary>
    /// <param name="service"> </param>
    /// <param name="queueSettings">The queue settings to use.</param>
    public void ProcessTempQueue(IOrganizationService service, QueueSetting queueSettings)
    {
      //Exit if 0
      if (queueSettings.TempQueueAge < 1)
        return;

      LogHelper.Output("Querying temp queue for items to remove...");
      var crmHelper = new CrmHelper(service);

      //Execute the query
      List<QueueItem> queueItems;
      try
      {
        var svcContext = new ServiceContext(service);
        var before = DateTime.UtcNow.AddHours(queueSettings.TempQueueAge - (queueSettings.TempQueueAge * 2));
        queueItems = crmHelper.GetQueueItems(new Guid(queueSettings.TempQueueId), queueSettings.TempQueueId, before);
      }
      catch (Exception ex)
      {
        LogHelper.Log("Error querying temp queue for removal.", ex, LogHelper.LogType.Exception);
        return;
      }

      LogHelper.Output("Found {0} e-mail on queue.", queueItems.Count.ToString());

      //Loop through result
      foreach (var item in queueItems)
      {
        try
        {
          //Remove e-mail
          crmHelper.Delete(item.ObjectId.Id);
        }
        catch (Exception ex)
        {
          LogHelper.Log("Unable to remove item from temp queue.", ex, LogHelper.LogType.Exception);
        }
      }
    }

    /// <summary>
    /// Replaces information in the mail to be sent
    /// </summary>
    /// <returns></returns>
    /// <remarks></remarks>
    private string ParseCaseMail(IOrganizationService service, string strText, Incident incident, IncomingEntity entity, Email email, Queue queue)
    {
      try
      {
        //Parse and replace information used in the e-mail. First the old way for backward compatibility...
        var crmHelper = new CrmHelper(service);
        var strParsedText = strText.Replace("{ticketnumber}", incident.TicketNumber).Replace("{title}", incident.Title).Replace("{firstname}", entity.FirstName).Replace("{lastname}", entity.LastName).Replace("{sender}", email.From.First().AddressUsed).Replace("{caseid}", incident.IncidentId.Value.ToString().ToLower()).Replace("{queuename}", queue.Name).Replace("{emailbodytext}", crmHelper.CleanHtml(email.Description)).Replace("{emailbodyhtml}", crmHelper.CleanHtml(email.Description).Replace("/n", "<br/>")).Replace("{date}", DateTime.Now.ToShortDateString()).Replace("{time}", DateTime.Now.ToShortTimeString()).Replace("{emailid}", email.ActivityId.Value.ToString().ToLower());

        //...and then using dynamic entity info
        if (_objParser == null)
          _objParser = new Regex("{#\\S*#}", RegexOptions.IgnoreCase);

        //Loop through all matches
        foreach (Match objMatch in _objParser.Matches(strParsedText))
        {
          var attributeName = objMatch.Value.Replace("{#", "").Replace("#}", "");
          if (incident.Attributes.ContainsKey(attributeName))
            strParsedText = strParsedText.Replace(objMatch.Value, incident[attributeName].ToString());
        }

        return strParsedText;
      }
      catch (Exception ex)
      {
        LogHelper.Log("Could not parse case mail.", ex, LogHelper.LogType.Exception);
        throw;
      }

    }
  }
}
